package lexer

import (
	"monkey/token"
)

type Lexer struct {
	// 输入的字符串
	input string
	// 当前的字符的位置，默认为0
	position int
	// 当前的字符的位置 + 1，m默认为0
	readPosition int
	// 当前的字符的byte
	ch byte
}

// New 初始化Lexer，并初始化position和readPosition指针
func New(input string) *Lexer {
	// 初始化Lexer
	l := &Lexer{
		input: input,
	}
	// 读取第一个字符，position指向第一个字符，readPosition指向第二个字符，ch指向下一个字符
	l.readChar()
	return l
}

// NextToken 获取当前字符对应的token，并前移position和readPosition指针，ch指向下一个字符
func (l *Lexer) NextToken() token.Token {
	var tok token.Token
	// 跳过空白字符
	l.skipWhitespace()

	// 判断当前字符对应的token
	// 当前字符就是l.ch指向的字符
	switch l.ch {
	// 当前字符是'='
	case '=':
		// 看一下下一个字符是不是也是'='，是的话，获取的token就是"=="
		if l.peekChar() == '=' {
			// 保存当前字符，需要与下一个字符合并起来组成"=="
			ch := l.ch
			// 迁移position和readPosition指针，指向下一个字符，ch指向下一个字符
			l.readChar()
			// 合并字符，literal = "=="
			literal := string(ch) + string(l.ch)
			// token = "=="
			tok = token.Token{
				Type:    token.EQ,
				Literal: literal,
			}
		} else {
			// token = "="
			tok = newToken(token.ASSIGN, l.ch)
		}
	case '+':
		// token = "+"
		tok = newToken(token.PLUS, l.ch)
	case '-':
		// token = "-"
		tok = newToken(token.MINUS, l.ch)
	case '!':
		// 看一下下一个字符是不是也是'='，是的话，获取的token就是"!="
		if l.peekChar() == '=' {
			// 保存当前字符，需要与下一个字符合并起来组成"!="
			ch := l.ch
			// 移动指针，使ch指向下一个字符
			l.readChar()
			// 合并字符，literal = "!="
			literal := string(ch) + string(l.ch)
			// token = "!="
			tok = token.Token{
				Type:    token.NOT_EQ,
				Literal: literal,
			}
		} else {
			// token = "!"
			tok = newToken(token.BANG, l.ch)
		}
	case '/':
		// token = "/"
		tok = newToken(token.SLASH, l.ch)
	case '*':
		// token = "*"
		tok = newToken(token.ASTERISK, l.ch)
	case '<':
		// token = "<"
		tok = newToken(token.LT, l.ch)
	case '>':
		// token = ">"
		tok = newToken(token.GT, l.ch)
	case ';':
		// token = ";"
		tok = newToken(token.SEMICOLON, l.ch)
	case ':':
		tok = newToken(token.COLON, l.ch)
	case ',':
		// token = ","
		tok = newToken(token.COMMA, l.ch)
	case '{':
		// token = "{"
		tok = newToken(token.LBRACE, l.ch)
	case '}':
		// token = "}"
		tok = newToken(token.RBRACE, l.ch)
	case '(':
		// token = "("
		tok = newToken(token.LPAREN, l.ch)
	case ')':
		// token = ")"
		tok = newToken(token.RPAREN, l.ch)
	case '"':
		tok.Type = token.STRING
		tok.Literal = l.readString()
	case '[':
		tok = newToken(token.LBRACKET, l.ch)
	case ']':
		tok = newToken(token.RBRACKET, l.ch)
	// 结束
	case 0:
		// token = "", tokenType = "EOF"
		tok.Literal = ""
		tok.Type = token.EOF
	default:
		// 标识符，关键字和数字
		// 如果当前字符是字母，则该token是标识符或关键字
		// 注意：现在还没有支持字符串类型
		if isLetter(l.ch) {
			// 读取标识符或关键字
			tok.Literal = l.readIdentifier()
			// 判断是不是标识符或关键字
			tok.Type = token.LookupIdent(tok.Literal)
			return tok
			// 	数字
			// 注意：现在只支持整数类型的数字
		} else if isDigit(l.ch) {
			// 读取数字
			tok.Literal = l.readNumber()
			tok.Type = token.INT
			return tok
		} else {
			// 非法此法单元
			tok = newToken(token.ILLEGAL, l.ch)
		}
	}
	// 前移指针，position指向下一个位置，l.ch指向下一个字符
	l.readChar()
	return tok
}

// 跳过空白字符，包括' '，'\t'，'\n'，'\r'，ch指向空白字符之后的第一个字符
func (l *Lexer) skipWhitespace() {
	// 如果当前字符是空白字符，就前移position和readPosition指针
	for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r' {
		l.readChar()
	}
}

// 读取当前position的字符，并前移position指针
func (l *Lexer) readChar() {
	// l.readPosition == len(l.input)时，l.input被读取完了。
	// readPosition是指向下一个字符的指针， 相比于当前字符的指针position + 1，
	// position是从0开始计算的，len(l.input)是从1开始计算的，
	// 所以len(l.input)的最大值 == position的最大值 + 1，也就是len(l.input)的最大值等于readPosition的最大值
	// 特殊情况：len(l.input)的最大值是0，也就是l.input == ""，这时候，执行New()初始化时后，ch = 0，表示读取完了
	if l.readPosition >= len(l.input) {
		// ch = 0 相当于结束，0在ASCII码表中是第一个字符，在此处是代表结束
		l.ch = 0
	} else {
		l.ch = l.input[l.readPosition]
	}
	// 读取一个字符后，当前位置position前移
	l.position = l.readPosition
	// readPosition始终比position + 1
	l.readPosition += 1
}

// 查看position向前的一个字符
func (l *Lexer) peekChar() byte {
	if l.readPosition >= len(l.input) {
		return 0
	} else {
		return l.input[l.readPosition]
	}
}

// 从当前字母或_开始，一直读取到非字母或_字符的前一个字符
// 读取标识符或关键字
func (l *Lexer) readIdentifier() string {
	// 当前的字符的位置，也就是标识符或关键字开始的位置
	position := l.position
	// 是字母或_就一直读取
	// 当读取到第一个非字母或_的字符时，l.position，也就是l.ch，指向的是标识符或关键字的结尾
	for isLetter(l.ch) {
		l.readChar()
	}
	// 到这里的时候，l.ch指向的是标识符或关键字的结尾
	return l.input[position:l.position]
}

// 从第一个数字开始，一直读取到非数字字符
func (l *Lexer) readNumber() string {
	position := l.position
	// 是数字就一直读取
	for isDigit(l.ch) {
		l.readChar()
	}
	// 到这里的时候，l.ch指向的是最后一个数字
	return l.input[position:l.position]
}

func (l *Lexer) readString() string {
	position := l.position + 1
	for {
		l.readChar()
		if l.ch == '"' || l.ch == 0 {
			break
		}
	}

	return l.input[position:l.position]
}

// 判断是不是字母或_
func isLetter(ch byte) bool {
	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
}

// 判断是不是数字
func isDigit(ch byte) bool {
	return '0' <= ch && ch <= '9'
}

// 构造新的token实例
func newToken(tokenType token.TokenType, ch byte) token.Token {
	return token.Token{
		Type:    tokenType,
		Literal: string(ch),
	}
}
